﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using DeepMixWorkflow;
using Microsoft.Phone.BackgroundAudio;
using Microsoft.Phone.Shell;
using MvvmBase.ViewModel;

namespace DeepmixRadioClient.Workflow
{
    public class Logic : ViewModelBase
    {
        private const string TileUri = @"isostore:/Shared/ShellContent/papar.jpg";
        private const string AlbumIconUri = @"isostore:/Shared/Media/papar.jpg";

        private readonly ThreadLocker _updateLocker = new ThreadLocker();
        private bool _errorOcured;
        private ImageSource _image;
        private bool _isInvokingPlayer;

        private TimeSpan _moscowTime;
        private DeepMixTrack _onAirTrackFromServer;
        private Timer _updateTimer;

        public Logic(IDispatcherOwner dispatcherOwner, INavigator navigator, Action<DeepMixTrack> bringIntoViewProgram,Action<DeepMixTrack> bringIntoViewSelected) : base(dispatcherOwner, navigator)
        {
            _bringIntoViewProgram = bringIntoViewProgram;
            _bringIntoViewSelected = bringIntoViewSelected;
            Application.Current.Exit += OnApplicationExit;
            CommonHelper.Log("����������� ������");
            PropertyChanged += OnPropertyChanged;

            StartWorking();
        }

        private string ImageName
        {
            get { return StorageContainer.ImageName.Value; }
            set
            {
                CommonHelper.Log("�������� ����������� ����� ��������: " + value);
                if (StorageContainer.ImageName.Value != value)
                {
                    StorageContainer.ImageName.Value = value;
                    if (value != null)
                        ProcessImageFromServer();
                }
            }
        }

        public bool IsError
        {
            get { return StorageContainer.IsError.Value; }
            set
            {
                if (StorageContainer.IsError.Value != value)
                {
                    StorageContainer.IsError.Value = value;
                    RaisePropertyChanged(() => IsError);
                }
            }
        }

        public DeepMixTrack[] Selected
        {
            get
            {
                if (!StorageContainer.Selected.Exists)
                {
                    NeedUpdateNow();
                }

                return StorageContainer.Selected.Value.Tracks;
            }
            set
            {
                StorageContainer.Selected.Value = new DeepMixTrackContainer {Tracks = value};
                SelectPlayingTrack();
                RaisePropertyChanged(() => Selected);
            }
        }

        public DeepMixTrack[] Program
        {
            get
            {
                if (!StorageContainer.Program.Exists)
                {
                    NeedUpdateNow();
                }

                return StorageContainer.Program.Value.Tracks;
            }
            private set
            {
                StorageContainer.Program.Value = new DeepMixTrackContainer {Tracks = value};
                SelectPlayingTrack();
                RaisePropertyChanged(() => Program);
            }
        }

        public DeepMixTrack CurrentOnAirTrack
        {
            get { return StorageContainer.OnAir.Value; }
            set
            {
                StorageContainer.OnAir.Value = value;
                SelectPlayingTrack();
                RaisePropertyChanged(() => CurrentOnAirTrack);
            }
        }

        private DeepMixTrack OnAirFromPlayerOrLastLoadedFromServer
        {
            get
            {
                if (PlayerWorkflow.PlayerTrack != null && PlayerWorkflow.PlayerTrack.Tag != null)
                {
                    if (PlayerWorkflow.Mode == DeepMixMode.OnAir)
                    {
                        return CommonHelper.FromAudioTrack(PlayerWorkflow.PlayerTrack);
                    }
                }

                if (_onAirTrackFromServer != null)
                {
                    return _onAirTrackFromServer;
                }

                return null;
            }
        }

        public string StatusText
        {
            get
            {
                if (_errorOcured) return AppResources.ConnectionError;
                PlayState playerState = PlayerWorkflow.Player.PlayerState;
                if (_isInvokingPlayer)
                    return AppResources.Connecting;
                if (playerState == PlayState.Paused && !CommonHelper.IsOnline())
                {
                    return AppResources.WaitForNet;
                }
                string playerStateText = playerState.ToText();
                if (PlayerWorkflow.PlayerTrack != null)
                {
                    string uri;
                    DeepMixMode mode = PlayerWorkflow.PlayerTrack.Tag.GetDeepMixMode(out uri);
                    playerStateText += " (";
                    switch (mode)
                    {
                        case DeepMixMode.Error:
                            return AppResources.ConnectionError;
                        case DeepMixMode.OnAir:
                            playerStateText += AppResources.Radio;
                            break;
                        case DeepMixMode.Selected:
                            playerStateText += AppResources.Selected;
                            break;
                        case DeepMixMode.Unknown:
                            return "";
                    }
                    playerStateText += ")";
                }
                return playerStateText;
            }
        }

        public ImageSource Image
        {
            get { return _image; }
            set
            {
                _image = value;
                RaisePropertyChanged(() => Image);
            }
        }

        public TimeSpan MoscowTime
        {
            get { return _moscowTime; }
            set
            {
                _moscowTime = value;
                RaisePropertyChanged(() => MoscowTime);
            }
        }

        private void OnApplicationExit(object sender, EventArgs eventArgs)
        {
            _updateTimer.Dispose();
        }

        public void ProgramClicked(DeepMixTrack track)
        {
            if (track.EqualsByArtistAndTitle(CurrentOnAirTrack))
            {
                PlayRadio();
            }
            else
            {
                DeepMixTrack[] tracks = Program;
                int length = tracks.Length;
                for (int i = 0; i < length - 1; i++)
                {
// ReSharper disable PossibleUnintendedReferenceComparison
                    if (track == tracks[i])
// ReSharper restore PossibleUnintendedReferenceComparison
                    {
                        DeepMixTrack trackPlusOne = tracks[i + 1];
                        if ((track.StartTime <= _moscowTime && _moscowTime <= trackPlusOne.StartTime) ||
                            (track.StartTime > trackPlusOne.StartTime &&
                             (_moscowTime >= track.StartTime || _moscowTime <= trackPlusOne.StartTime)))
                        {
                            MessageBox.Show(AppResources.NotBegan);
                            return;
                        }
                    }
                }
                OnTimeLite();
            }
        }

        public event EventHandler TimeLite;

        public void OnTimeLite()
        {
            EventHandler handler = TimeLite;
            if (handler != null) handler(this, EventArgs.Empty);
        }

        private static void NeedUpdateNow()
        {
            StorageContainer.RefreshTime.Value = DateTime.Now;
        }

        private void StartWorking()
        {
            InitProgressIndicator();

            PlayerWorkflow.Player.PlayStateChanged += OnPlayerStateChanged;

            _updateTimer = new Timer(updateTimerTicked, null, TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(1));

            TryLoadImageFromStorageIntoModel();
            if (!StorageContainer.IsTileUpdated.Exists)
            {
                TryUpdateTileFromStorage();
            }
        }

        private void updateTimerTicked(object state)
        {
            MoscowTime = DateTime.UtcNow.AddHours(4).Add(StorageContainer.LocalTimeShift.Value).TimeOfDay;
            
            _updateLocker.Lock(() =>
                {
                    if (IsBusy || IsSmallBusy) return;
                    var onAirJob = (ModelJob) (state ?? CreateNewJob(ModeJobMode.Low));

                    onAirJob.Released += (sender, args) =>
                        {
                            if (!IsError)
                            {
                                ModelJob modelJob = CreateNewJob(ModeJobMode.Low);
                                RunUpdateSelectedFromServer(modelJob);
                            }
                        };

                    RunUpdateOnAirFromServer(onAirJob);
                });
        }

        private void RunUpdateSelectedFromServer(ModelJob modelJob)
        {
            DateTime currentDate = DateTime.Now.Date;
            if (currentDate >= StorageContainer.RefreshSelectedDate.Value)
            {
                CommonHelper.GetSelected(tracks =>
                    {
                        StorageContainer.RefreshSelectedDate.Value = DateTime.Now.Date.AddDays(1);
                        Selected = tracks;
                        modelJob.Release();
                    }, exception =>
                        {
#if DEBUG
                            throw exception;
#else
                        modelJob.Release();
#endif
                        });
            }
            else
            {
                modelJob.Release();
            }
        }

        private void RunUpdateOnAirFromServer(ModelJob modelJob)
        {
            CommonHelper.Log("��������� ������");

            modelJob.Released += (sender, args) => CalculateOnAirTrack();

            DateTime refreshTime = StorageContainer.RefreshTime.Value;
            DateTime timeOfDay = DateTime.Now;

            if (timeOfDay >= refreshTime ||
                timeOfDay.AddHours(2) < refreshTime)
            {
                CommonHelper.Log("������ ����� ���������� � �������.");
                CommonHelper.GetNowPlayingParser(parser =>
                    {
                        CommonHelper.Log("������ ������ ���������, �������� ������");
                        _onAirTrackFromServer = parser.GetNowPlayingTrack();
                        CommonHelper.Log(
                            "������� ��������� ���� � �����, ��������� ��� � ��������� �����");

                        TimeSpan moscowTime = parser.GetMoscowTime();
                        TimeSpan localMoscowTime =
                            DateTime.UtcNow.TimeOfDay.Add(
                                TimeSpan.FromHours(4));
                        StorageContainer.LocalTimeShift.Value = moscowTime - localMoscowTime;

                        //��������� �� ��������� ����� ���������� �����)
                        var program = new List<DeepMixTrack>(4);
                        DeepMixTrack[] deepMixTracks = parser.GetProgram();
                        program.AddRange(deepMixTracks);

                        string imageName = parser.GetImagePath();
                        ImageName = imageName;

                        TimeSpan nextTrackTime = moscowTime.Add(TimeSpan.FromHours(1));
                        foreach (DeepMixTrack track in program)
                        {
                            if (track.StartTime > moscowTime)
                            {
                                nextTrackTime = track.StartTime;
                                break;
                            }
                        }

                        TimeSpan dif = nextTrackTime > moscowTime
                                           ? nextTrackTime - moscowTime
                                           : TimeSpan.FromHours(24) - moscowTime + nextTrackTime;

                        dif = dif.Add(TimeSpan.FromMinutes(1));

                        CommonHelper.Log(string.Format(
                            "��������� ����� ��������� ����������: {0}(first program) - {1}(moscowTime) + {2}(local now)+2 ������ �� ������..",
                            nextTrackTime, moscowTime, DateTime.Now.TimeOfDay));
                        StorageContainer.RefreshTime.Value = DateTime.Now + dif;

                        Program = program.ToArray();

                        IsError = false;

                        modelJob.Release();
                    }, exception =>
                        {
                            IsError = true;
                            StorageContainer.RefreshTime.Value = DateTime.Now.Add(TimeSpan.FromSeconds(20));
#if DEBUG
                            throw exception;
#else
                        modelJob.Release();
#endif
                        });
            }
            else
            {
                modelJob.Release();
            }
        }

        #region Busy Indicator
        private static void InitProgressIndicator()
        {
            if (SystemTray.ProgressIndicator == null)
            {
                SystemTray.ProgressIndicator = new ProgressIndicator();
            }
            SystemTray.IsVisible = true;
            SystemTray.ProgressIndicator.IsIndeterminate = true;
            SystemTray.ProgressIndicator.Text = AppResources.Loading;
        }

        private void IsBusyChanged()
        {
            UpdateIndicator();
        }

        private void OnPropertyChanged(object sender3, PropertyChangedEventArgs args3)
        {
            if (args3.PropertyName == "IsBusy" || args3.PropertyName == "IsSmallBusy")
            {
                IsBusyChanged();
            }
        }

        private void UpdateIndicator()
        {
            bool isReallyBusy = IsBusy || IsSmallBusy;

            if (SystemTray.ProgressIndicator != null)
            {
                SystemTray.ProgressIndicator.IsVisible = isReallyBusy;
                ((App)Application.Current).RootFrame.IsEnabled = !IsBusy;
            }
        } 
        #endregion

        private void ProcessImageFromServer()
        {
            var webClient = new WebClient {AllowReadStreamBuffering = true};
            webClient.OpenReadCompleted += (sender, args) =>
                {
                    if (args.Error == null)
                    {
                        StorageContainer.Image.DoWriteNew(stream =>
                            {
                                int count;
                                var buffer = new byte[4096];
                                do
                                {
                                    count = args.Result.Read(buffer, 0, 4096);
                                    if (count > 0) stream.Write(buffer, 0, count);
                                } while (count != 0);
                            });

                        TryUpdateTileFromStorage();
                        TryLoadImageFromStorageIntoModel();
                    }
                    else
                    {
                        ImageName = null;
                    }
                };
            webClient.OpenReadAsync(new Uri(ImageName, UriKind.Absolute));
        }

        private static void TryUpdateTileFromStorage()
        {
            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (StorageContainer.Image.Exists)
                {
                    storage.CopyFile(StorageContainer.Image.FileName, @"Shared\ShellContent\papar.jpg", true);
                    storage.CopyFile(StorageContainer.Image.FileName, @"Shared\media\papar.jpg", true);
                    var shellTileData = new StandardTileData
                        {
                            BackContent = ""
                        };

                    var tileBack = new Uri(TileUri, UriKind.Absolute);

                    shellTileData.BackBackgroundImage = tileBack;
                    shellTileData.BackTitle = "";

                    ShellTile tile = ShellTile.ActiveTiles.First();
                    tile.Update(shellTileData);

                    StorageContainer.IsTileUpdated.Value = true;
                }
            }
        }

        public void TryLoadImageFromStorageIntoModel()
        {
            ExectuteIfCan(() =>
                {
                    if (StorageContainer.Image.Exists)
                    {
                        var image = new BitmapImage();
                        StorageContainer.Image.DoReadFromBegin(stream =>
                                                                   {
                                                                       try
                                                                       {
                                                                           image.SetSource(stream);
                                                                       }
                                                                       catch 
                                                                       {
                                                                       }
                                                                   });
                        Image = image;
                    }
                    else
                    {
                        CommonHelper.Log(
                            "�� ������� ������� ��������, ������� ��������� null, ��� � � ����. ��� ���������� ��� � ��� ���������");
                        ImageName = null;
                    }
                });
        }

        private void CalculateOnAirTrack()
        {
            DeepMixTrack onAirFromPlayerOrLastLoadedFromServer = OnAirFromPlayerOrLastLoadedFromServer;
            if ((CurrentOnAirTrack != null || onAirFromPlayerOrLastLoadedFromServer == null) &&
                (CurrentOnAirTrack == null ||
                 CurrentOnAirTrack.EqualsByArtistAndTitle(onAirFromPlayerOrLastLoadedFromServer) ||
                 onAirFromPlayerOrLastLoadedFromServer == null)) return;
            CommonHelper.Log("����� �� ���������: {0} <= {1}", CurrentOnAirTrack,
                             onAirFromPlayerOrLastLoadedFromServer);

            CurrentOnAirTrack = onAirFromPlayerOrLastLoadedFromServer;
        }


        private void OnPlayerStateChanged(object sender, EventArgs eventArgs)
        {
            if (PlayerWorkflow.Player.PlayerState != PlayState.Unknown)
                _isInvokingPlayer = false;

            string uri;
            if (PlayerWorkflow.PlayerTrack != null)
                _errorOcured = PlayerWorkflow.PlayerTrack.Tag.GetDeepMixMode(out uri) == DeepMixMode.Error;

            InvokePlayStatusTextUpdated();
            UpdateButtons();
        }

        private readonly Action<DeepMixTrack> _bringIntoViewProgram;
        private readonly Action<DeepMixTrack> _bringIntoViewSelected;

        public void SelectPlayingTrack()
        {
            ExectuteIfCan(() => { 
            if (Selected == null || Program == null)
                return;

            UnSelectAll();

            AudioTrack track = null;
            try
            {
                track = BackgroundAudioPlayer.Instance.Track;
            }
            catch (InvalidOperationException)
            {
            }


            if (track != null)
                if (track.Tag != null)
                {
                    string uri;
                    DeepMixMode mode = track.Tag.GetDeepMixMode(out uri);
                    if (mode == DeepMixMode.OnAir)
                    {
                        DeepMixTrack nowTrack = CommonHelper.FromAudioTrack(track);

                        foreach (DeepMixTrack obj3 in Program)
                        {
                            var track2 = obj3;
                            if (track2.Equals(nowTrack))
                            {
                                track2.IsSelected = true;
                                _bringIntoViewProgram(track2);
                                
                                break;
                            }
                        }
                    }
                    if (mode == DeepMixMode.Selected)
                    {
                        foreach (DeepMixTrack obj3 in Selected)
                        {
                            var track2 = obj3;
                            if (track2.Link == uri)
                            {
                                track2.IsSelected = true;
                                _bringIntoViewSelected(track2);
                                break;
                            }
                        }
                    }
                }
            });
        }

        private void UnSelectAll()
        {
            foreach (DeepMixTrack obj3 in Selected)
            {
                var track2 = obj3;
                track2.IsSelected = false;
            }

            foreach (DeepMixTrack obj3 in Program)
            {
                var track2 = obj3;
                track2.IsSelected = false;
            }
        }

        public void PlaySelectedTrack(DeepMixTrack deepMixTrack)
        {
            UnSelectAll();
            deepMixTrack.IsSelected = true;
            BackgroundAudioPlayer.Instance.Volume = 1;
            CommonHelper.Log("������ �� ��������������� ���������� ����� " + deepMixTrack.Link);
            LockPlay();
            string tag = TagParser.CreateSelected(deepMixTrack.Link);
            var audioTrack = new AudioTrack(null, deepMixTrack.Title, deepMixTrack.Artist, AppResources.Selected, null,
                                            tag,
                                            EnabledPlayerControls.SkipNext | EnabledPlayerControls.SkipPrevious |
                                            EnabledPlayerControls.Pause);
            CommonHelper.Log(audioTrack.AboutTrack());
            PlayerWorkflow.Player.Track = audioTrack;
        }

        private void LockPlay()
        {
            InvokeLockPlayAndSkip();
            _isInvokingPlayer = true;
            _errorOcured = false;
            InvokePlayStatusTextUpdated();
        }

        public void PlayRadio()
        {
            BackgroundAudioPlayer.Instance.Volume = 1;
            CommonHelper.Log("������ �� ��������������� �����");
            LockPlay();

            ModelJob modelJob = CreateNewJob();
            modelJob.Released += (sender, args) =>
                {
                    if (IsError)
                    {
                        _isInvokingPlayer = false;
                        InvokePlayStatusTextUpdated();
                        return;
                    }
                    string onAirTag = TagParser.CreateOnAir();
                    DeepMixTrack onAirTrack = CurrentOnAirTrack ??
                                              new DeepMixTrack
                                                  {
                                                      Title = "Deep Mix Radio Moscow",
                                                      Artist = AppResources.DeeMixSite
                                                  };
                    PlayerWorkflow.Player.Track = new AudioTrack(null, onAirTrack.Title, onAirTrack.Artist, AppResources.Radio,
                                                  new Uri(AlbumIconUri),
                                                  onAirTag, EnabledPlayerControls.Pause);
                };
            RunUpdateOnAirFromServer(modelJob);
        }


        #region Commands
        public bool IsPrevEnabled
        {
            get
            {
                if (_isInvokingPlayer) return false;
                if (PlayerWorkflow.PlayerTrack != null)
                    return (PlayerWorkflow.PlayerTrack.PlayerControls & EnabledPlayerControls.SkipPrevious) ==
                           EnabledPlayerControls.SkipPrevious;
                return false;
            }
        }

        public bool IsNextEnabled
        {
            get
            {
                if (_isInvokingPlayer) return false;
                if (PlayerWorkflow.PlayerTrack != null)
                    return (PlayerWorkflow.PlayerTrack.PlayerControls & EnabledPlayerControls.SkipNext) ==
                           EnabledPlayerControls.SkipNext;
                return false;
            }
        }

        public bool IsStopEnabled
        {
            get
            {
                PlayState playerState = PlayerWorkflow.Player.PlayerState;
                return playerState != PlayState.Stopped && playerState != PlayState.Unknown &&
                       playerState != PlayState.Error;
            }
        }

        /// <summary>
        ///   true - ����� ������,false ����� �������,null - ������ ������
        /// </summary>
        public bool? IsPlayOrPause
        {
            get
            {
                if (_isInvokingPlayer) return null;

                if (IsStopEnabled) return null;
                switch (PlayerWorkflow.Player.PlayerState)
                {
                    case PlayState.Unknown:
                    case PlayState.Paused:
                    case PlayState.Stopped:
                        {
                            if (PlayerWorkflow.PlayerTrack == null) return true; //��������� ������ - �������������� �����

                            if (PlayerWorkflow.PlayerTrack != null)
                            {
                                DeepMixMode deepMixMode = PlayerWorkflow.Mode;
                                if (deepMixMode != DeepMixMode.Error && deepMixMode != DeepMixMode.Unknown)
                                {
                                    return true;
                                }
                            }
                            return null;
                        }
                    case PlayState.BufferingStarted:
                    case PlayState.Error:
                    case PlayState.Shutdown:
                    case PlayState.TrackReady:
                    case PlayState.TrackEnded:
                        return null;
                    case PlayState.BufferingStopped:
                        return null;
                    case PlayState.Playing:
                        if (PlayerWorkflow.Mode == DeepMixMode.OnAir) return null;
                        return false;
                    default:
                        return false;
                }
            }
        }
       


        public void InvokePlayStatusTextUpdated()
        {
            SelectPlayingTrack();
            RaisePropertyChanged(() => StatusText);
        }

        private void UpdateButtons()
        {
            InvokeIsNextEnabledChanged();
            InvokeIsPlayOrPauseChanged();
            InvokeIsPrevEnabledChanged();
            InvokeIsStopEnabledChanged();
        }

        public event Action IsPrevEnabledChanged;

        private void InvokeIsPrevEnabledChanged()
        {
            Action handler = IsPrevEnabledChanged;
            if (handler != null) handler();
        }

        public event Action IsNextEnabledChanged;

        private void InvokeIsNextEnabledChanged()
        {
            Action handler = IsNextEnabledChanged;
            if (handler != null) handler();
        }

        public event Action IsPlayOrPauseChanged;

        private void InvokeIsPlayOrPauseChanged()
        {
            Action handler = IsPlayOrPauseChanged;
            if (handler != null) handler();
        }

        public event Action IsStopEnabledChanged;

        private void InvokeIsStopEnabledChanged()
        {
            Action handler = IsStopEnabledChanged;
            if (handler != null) handler();
        }

        public void GoPrev()
        {
            if (IsPrevEnabled)
            {
                InvokeLockPlayAndSkip();
                BackgroundAudioPlayer.Instance.SkipPrevious();
                InvokeLockPlayAndSkip();
                _isInvokingPlayer = true;
                InvokePlayStatusTextUpdated();
            }
        }

        public void GoNext()
        {
            if (IsNextEnabled)
            {
                InvokeLockPlayAndSkip();
                BackgroundAudioPlayer.Instance.SkipNext();
                InvokeLockPlayAndSkip();
                _isInvokingPlayer = true;
                InvokePlayStatusTextUpdated();
            }
        }

        public void Stop()
        {
            if (IsStopEnabled)
            {
                BackgroundAudioPlayer.Instance.Stop();
            }
        }

        public event Action LockPlayAndSkip;

        public void InvokeLockPlayAndSkip()
        {
            Action handler = LockPlayAndSkip;
            if (handler != null) handler();
        }
        
        public void PlayClicked()
        {
            if (IsPlayOrPause == null) return;
            var isPlay = (bool) IsPlayOrPause;
            if (isPlay)
            {
                InvokeLockPlayAndSkip();
                if (BackgroundAudioPlayer.Instance.Track == null || PlayerWorkflow.Mode == DeepMixMode.OnAir)
                {
                    PlayRadio();
                    InvokeLockPlayAndSkip();
                }
                else
                {
                    BackgroundAudioPlayer.Instance.Play();
                    _isInvokingPlayer = true;
                    InvokePlayStatusTextUpdated();
                }
            }
            else
            {
                BackgroundAudioPlayer.Instance.Pause();
                InvokePlayStatusTextUpdated();
            }
        }

        public void UpdateAll()
        {
            StorageContainer.OnAir.Value = new DeepMixTrack();
            NeedUpdateNow();
            StorageContainer.RefreshSelectedDate.Value = DateTime.Now.Date;
            updateTimerTicked(CreateNewJob());
        }

        #endregion
    }
}